﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Util.Web
{
    // V 1.0.2 - 2025-11-07 15:16:01
    // 1. Receive 和 StandardReceive 的返回值由 string 改变为 byte[]
    // 2. 新增一个静态方法 ByteArr2StringViaHoweStandard 函数, Howe定义的发送信息最后需要截取结束符号, 用这个函数进行 ByteArr --> String
    // 
    // V 1.0.1 - 2022-09-08 13:46:32
    // 根据 https://github.com/BrandonPotter/SimpleTCP 中的读取方法, 改写了 TcpClientUtils.StandardReceive 的方法,
    // 尝试解决粘包问题

    /// <summary>
    /// TcpClientUtils
    /// </summary>
    public class TcpClientUtils
    {
        private static int bufferSize = 8192;
        /// <summary>
        /// 大多数文件系统都配置为使用4096或8192的块大小。
        /// 理论上，如果配置缓冲区大小使得读取比磁盘块多几个字节，
        /// 则使用文件系统的操作可能效率极低（即，如果您将缓冲区配置为一次读取4100个字节，
        /// 每次读取将需要文件系统进行2次块读取。如果块已经在缓存中，
        /// 那么你最终会支付RAM的价格 - > L3 / L2缓存延迟。
        /// 如果你运气不好并且块还没有缓存，那么你也需要支付磁盘 - > RAM延迟的价格。
        /// </summary>
        public static int s_BufferSize
        {
            get
            {
                return bufferSize;
            }
            set
            {
                bufferSize = value;
            }
        }

        public static char s_StartChar
        {
            get
            {
                return (char)0x02;
            }
        }

        public static char s_EndChar
        {
            get
            {
                return (char)0x03;
            }
        }

        /// <summary>
        /// 接收 ( 解决TCP粘包问题, 因为提前知道数据包的大小 )
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        public static byte[] Receive(System.Net.Sockets.TcpClient tcpClient)
        {
            using (System.IO.MemoryStream msContent = new System.IO.MemoryStream())
            {
                int totalBytesRead = 0; // 读取总长度 -- 用于累积超出 s_BufferSize 时
                int startCharIndex = -1;
                int endCharIndex = -1;


                byte[] buffOfNetworkStream = new byte[s_BufferSize];
                int lastestBytesRead = 0; // 当前读取总长度

                System.Net.Sockets.NetworkStream networkStream = tcpClient.GetStream();
                
                lastestBytesRead = networkStream.Read(buffOfNetworkStream, 0, s_BufferSize);
                totalBytesRead = totalBytesRead + lastestBytesRead;

                // 定位 StartChar
                for (int i = 0; i < buffOfNetworkStream.Length; i++)
                {
                    if (s_StartChar == Convert.ToChar(buffOfNetworkStream[i]))
                    {
                        startCharIndex = i;
                        break;
                    }
                }

                if (startCharIndex < 0)
                {
                    throw new Exception("缺少(Char)Start");
                }

                // 获取内容长度 ( int类型, 共 4 个字节 )
                int contentLength = BitConverter.ToInt32(buffOfNetworkStream, startCharIndex + 1); // 内容长度

                // 首次获取写入内容
                msContent.Write
                (
                    buffOfNetworkStream,
                    startCharIndex + 1 + 4, // (Char)Start 起始位置 + 1( (char)Start 1 个字节 ) + 4( 内容长度 4 个字节 )
                    lastestBytesRead - (startCharIndex + 1 + 4)
                );

                while (totalBytesRead <
                    1 + 4 + contentLength + 1) // 1( (char)Start 1 个字节 ) + 4( 内容长度 4 个字节 ) + contentLength ( 读取出来的长度信息 ) + 1( (char)End 1 个字节 )
                {
                    lastestBytesRead = networkStream.Read(buffOfNetworkStream, 0, Util.Web.TcpClientUtils.s_BufferSize); // 再次获取剩余的内容
                    totalBytesRead = totalBytesRead + lastestBytesRead;
                    msContent.Write(buffOfNetworkStream, 0, lastestBytesRead);
                }

                networkStream = null; // !!!遇到的坑!!! 不要释放, 否则不能持续通信

                byte[] r = msContent.GetBuffer();
                return r;
                
            }
        }

        /// <summary>
        /// 使用Howe定义的方式拆解 arr, 得出字符串信息
        /// </summary>
        /// <param name="arr"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static string ByteArr2StringViaHoweStandard(byte[] arr, Encoding encoding = null)
        {
            string r = string.Empty;

            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }

            if (encoding != null)
            {
                r = encoding.GetString(arr, 0, arr.Length);
            }
            else
            {
                r = Encoding.UTF8.GetString(arr, 0, arr.Length);
            }

            // 定位 EndChar
            int endCharIndex = r.IndexOf(s_EndChar);
            if (endCharIndex < 0)
            {
                throw new Exception("缺少(Char)End");
            }

            r = r.Substring(0, endCharIndex);

            return r;
        }

        /// <summary>
        /// 接收 ( 解决TCP粘包问题, 因为提前知道数据包的大小 )
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="encoding">默认UTF8</param>
        /// <returns></returns>
        [Obsolete]
        public static string ReceiveV1(System.Net.Sockets.TcpClient tcpClient, System.Text.Encoding encoding = null)
        {
            string r = string.Empty;

            using (System.IO.MemoryStream msContent = new System.IO.MemoryStream())
            {
                int totalBytesRead = 0; // 读取总长度 -- 用于累积超出 s_BufferSize 时
                int startCharIndex = -1;
                int endCharIndex = -1;


                byte[] buffOfNetworkStream = new byte[s_BufferSize];
                int lastestBytesRead = 0; // 当前读取总长度

                System.Net.Sockets.NetworkStream networkStream = tcpClient.GetStream();

                lastestBytesRead = networkStream.Read(buffOfNetworkStream, 0, s_BufferSize);
                totalBytesRead = totalBytesRead + lastestBytesRead;

                // 定位 StartChar
                for (int i = 0; i < buffOfNetworkStream.Length; i++)
                {
                    if (s_StartChar == Convert.ToChar(buffOfNetworkStream[i]))
                    {
                        startCharIndex = i;
                        break;
                    }
                }

                if (startCharIndex < 0)
                {
                    throw new Exception("缺少(Char)Start");
                }

                // 获取内容长度 ( int类型, 共 4 个字节 )
                int contentLength = BitConverter.ToInt32(buffOfNetworkStream, startCharIndex + 1); // 内容长度

                // 首次获取写入内容
                msContent.Write
                (
                    buffOfNetworkStream,
                    startCharIndex + 1 + 4, // (Char)Start 起始位置 + 1( (char)Start 1 个字节 ) + 4( 内容长度 4 个字节 )
                    lastestBytesRead - (startCharIndex + 1 + 4)
                );

                while (totalBytesRead <
                    1 + 4 + contentLength + 1) // 1( (char)Start 1 个字节 ) + 4( 内容长度 4 个字节 ) + contentLength ( 读取出来的长度信息 ) + 1( (char)End 1 个字节 )
                {
                    lastestBytesRead = networkStream.Read(buffOfNetworkStream, 0, Util.Web.TcpClientUtils.s_BufferSize); // 再次获取剩余的内容
                    totalBytesRead = totalBytesRead + lastestBytesRead;
                    msContent.Write(buffOfNetworkStream, 0, lastestBytesRead);
                }

                byte[] contentByteArr = msContent.GetBuffer();

                if (encoding != null)
                {
                    r = encoding.GetString(contentByteArr, 0, contentByteArr.Length);
                }
                else
                {
                    r = Encoding.UTF8.GetString(contentByteArr, 0, contentByteArr.Length);
                }

                // 定位 EndChar
                endCharIndex = r.IndexOf(s_EndChar);
                if (endCharIndex < 0)
                {
                    throw new Exception("缺少(Char)End");
                }

                r = r.Substring(0, endCharIndex);

                // 释放
                networkStream = null;

                return r;
            }
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="toSend">发送内容</param>
        /// <param name="encoding">默认UTF-8</param>
        public static void Send(System.Net.Sockets.TcpClient tcpClient, string toSend, System.Text.Encoding encoding = null)
        {
            if (tcpClient == null)
            {
                return;
            }

            if (tcpClient.Connected == false)
            {
                return;
            }

            System.Net.Sockets.NetworkStream ns = tcpClient.GetStream();

            // 内容转换
            byte[] strBuffer = null;
            if (encoding != null)
            {
                strBuffer = encoding.GetBytes(toSend);
            }
            else
            {
                strBuffer = Encoding.UTF8.GetBytes(toSend);
            }

            // 发送
            TcpClientModel socketModel = new Util.Web.TcpClientModel(strBuffer);
            byte[] buffer = socketModel.ToByteArray();
            ns.Write(buffer, 0, buffer.Length);

            buffer = null;
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="toSend">发送内容</param>
        /// <param name="encoding">默认UTF-8</param>
        public static void Send(System.Net.Sockets.TcpClient tcpClient, byte[] toSend)
        {
            if (tcpClient == null)
            {
                return;
            }

            if (tcpClient.Connected == false)
            {
                return;
            }

            System.Net.Sockets.NetworkStream ns = tcpClient.GetStream();

            // 发送
            TcpClientModel socketModel = new Util.Web.TcpClientModel();
            socketModel.Content = toSend;
            byte[] buffer = socketModel.ToByteArray();
            ns.Write(buffer, 0, buffer.Length);

            buffer = null;
        }

        #region 标准接收&发送

        /// <summary>
        /// 标准接收
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        public static byte[] StandardReceive(System.Net.Sockets.TcpClient tcpClient)
        {
            using (System.IO.MemoryStream msContent = new System.IO.MemoryStream())
            {
                // 尝试解决 StandardReceiveV1 的缺点 粘包问题
                // 2022-09-08 10:58:46 新版采用 https://github.com/BrandonPotter/SimpleTCP 中的读取方法
                List<byte> bytesReceived = new List<byte>(1024 * 8);
                while (tcpClient.Available > 0 && tcpClient.Connected)
                {
                    byte[] nextByte = new byte[1];
                    tcpClient.Client.Receive(nextByte, 0, 1, System.Net.Sockets.SocketFlags.None);
                    bytesReceived.AddRange(nextByte);
                }

                if (bytesReceived.Count == 0)
                {
                    return null;
                }

                byte[] r = bytesReceived.ToArray();
                return r;
            }
        }

        /// <summary>
        /// 标准接收
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="encoding">默认UTF8</param>
        /// <returns></returns>
        [Obsolete]
        public static string StandardReceiveV2(System.Net.Sockets.TcpClient tcpClient, System.Text.Encoding encoding = null)
        {
            if (encoding == null) { encoding = Encoding.UTF8; }

            using (System.IO.MemoryStream msContent = new System.IO.MemoryStream())
            {
                // 尝试解决 StandardReceiveV1 的缺点 粘包问题
                // 2022-09-08 10:58:46 新版采用 https://github.com/BrandonPotter/SimpleTCP 中的读取方法
                List<byte> bytesReceived = new List<byte>(1024 * 8);
                while (tcpClient.Available > 0 && tcpClient.Connected)
                {
                    byte[] nextByte = new byte[1];
                    tcpClient.Client.Receive(nextByte, 0, 1, System.Net.Sockets.SocketFlags.None);
                    bytesReceived.AddRange(nextByte);
                }

                if (bytesReceived.Count == 0)
                {
                    return null;
                }

                var byteArr = bytesReceived.ToArray();
                return encoding.GetString(byteArr, 0, byteArr.Length);
            }
        }

        /// <summary>
        /// [弃用] 旧代码备份
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="encoding"></param>
        /// <param name="endSymbol"></param>
        /// <returns></returns>
        [Obsolete]
        public static string StandardReceiveV1(System.Net.Sockets.TcpClient tcpClient, System.Text.Encoding encoding = null, string endSymbol = null)
        {
            StringBuilder r = new StringBuilder(capacity: TcpClientUtils.s_BufferSize);

            if (encoding != null) { encoding = Encoding.UTF8; }
            if (endSymbol == null) { endSymbol = "\0"; }

            using (System.IO.MemoryStream msContent = new System.IO.MemoryStream())
            {
                int totalBytesRead = 0; // 读取总长度 由于发送的内容没有任何定位规则, 只能读取到 Buff 缓存的长度
                int lastestBytesRead = 0; // 当前读取总长度

                System.Net.Sockets.NetworkStream networkStream = tcpClient.GetStream();

                while (true)
                {
                    byte[] buffOfNetworkStream = new byte[s_BufferSize];
                    lastestBytesRead = networkStream.Read(buffOfNetworkStream, 0, s_BufferSize);
                    totalBytesRead = totalBytesRead + lastestBytesRead;

                    string temp = encoding.GetString(buffOfNetworkStream, 0, buffOfNetworkStream.Length);

                    if (temp.LastIndexOf(endSymbol) >= 0) // [坑] 如果buffer 的 长度刚刚好等于 内容长度, 会导致没有 \0
                    {
                        r.Append(temp.Trim('\0')); // 去掉多出来的 \0
                        break;
                    }
                    else
                    {
                        r.Append(temp);
                    }
                }

                // 释放
                networkStream = null;
                return r.ToString();
            }
        }

        /// <summary>
        /// 标准发送
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="toSend">发送内容</param>
        /// <param name="encoding">默认UTF-8</param>
        public static void StandardSend(System.Net.Sockets.TcpClient tcpClient, string toSend, System.Text.Encoding encoding = null)
        {
            if (tcpClient == null)
            {
                return;
            }

            if (tcpClient.Connected == false)
            {
                return;
            }

            System.Net.Sockets.NetworkStream ns = tcpClient.GetStream();

            // 内容转换
            byte[] strBuffer = null;
            if (encoding != null)
            {
                strBuffer = encoding.GetBytes(toSend);
            }
            else
            {
                strBuffer = Encoding.UTF8.GetBytes(toSend);
            }

            ns.Write(strBuffer, 0, strBuffer.Length);

            strBuffer = null;
        }

        /// <summary>
        /// 标准发送
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="toSend">发送内容</param>
        public static void StandardSend(System.Net.Sockets.TcpClient tcpClient, byte[] toSend)
        {
            if (tcpClient == null)
            {
                return;
            }

            if (tcpClient.Connected == false)
            {
                return;
            }

            System.Net.Sockets.NetworkStream ns = tcpClient.GetStream();

            ns.Write(toSend, 0, toSend.Length);

            toSend = null;
        }

        #endregion
    }
}
